/*
 * {comment of class}
 *
 * @author (C) <a href="http://www.carfield.com.hk">Carfield Yim </a>, 2004
 */
package hk.com.carfield.item;

import hk.com.carfield.content.Content;
import hk.com.carfield.content.Image;
import hk.com.carfield.core.ContentManager;
import hk.com.carfield.core.Context;
import hk.com.carfield.core.ItemManager;
import hk.com.carfield.util.FileUtil;
import hk.com.carfield.util.LogUtil;
import hk.com.carfield.util.UrlUtil;
import hk.com.carfield.web.Request;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;

import org.apache.commons.lang.StringEscapeUtils;

public class Directory implements Item {
	private static volatile String[] allItems = new String[0];

	public static final void init(final TreeMap<Long, String> sortedMap) {
		Directory.allItems = sortedMap.values().toArray(new String[0]);/*
																		 * should
																		 * clear
																		 * after
																		 * updated
																		 * ...
																		 */
	}

	public static String slideShowJS(final Context c, final File directory)
			throws IOException {
		final File[] childs = directory.listFiles(c.config.displayFilter());
		final StringBuffer sb = new StringBuffer();
		int imageCount = 0;
		if (childs != null) {
			sb.append("<script type=\"text/javascript\">");
			for (int fileCount = 0; fileCount < childs.length; fileCount++) {
				final Content content = ContentManager.getContent(c,
						childs[fileCount]);
				if (content instanceof Image) {
					final Item item = ItemManager.getItem(c, childs[fileCount]);
					sb.append("photo_urls[")
							.append(imageCount + 1)
							.append("]='")
							.append(UrlUtil.getUrl(c.config, childs[fileCount]))
							.append("';\n");
					sb.append("photo_captions[")
							.append(imageCount + 1)
							.append("]='")
							.append(StringEscapeUtils.escapeJavaScript(item
									.getName())).append("';\n");
					imageCount++;
				}
			}
			sb.append(" photo_count = " + imageCount
					+ "; \npreload_photo(1);\n</script>");
		}
		return imageCount == 0 ? "" : sb.toString();
	}

	private final Context c;

	private final File directory;

	private final SortedSet<Item> itemSet = ItemManager.getSortedSet();

	private final int pageNo, itemsOfPage;

	private final List<Integer> pages = new ArrayList<Integer>();

	private final Request request;

	public Directory(final Context c, final File f, final Request req,
			final int pageNo) {
		if (!f.isDirectory()) {
			LogUtil.error(new IOException("File is not normal directory"),
					c.mailMan, f);
		}
		directory = f;
		request = req;
		this.c = c;
		this.pageNo = pageNo;
		itemsOfPage = req.itemsOfPage();
	}

	public SortedSet<Item> getChildren() throws IOException {
		if (itemSet.size() == 0) {
			setItemSet();
		}
		return itemSet;
	}

	public final String getCssClass() {
		return "folder";
	}

	public final Date getDate() {
		return new Date(directory.lastModified());
	}

	public String getDesc() throws IOException {
		return c.config.getHeader(directory);
	}

	public final String getId() {
		return FileUtil.getId(c.config, directory);
	}

	public final String getName() {
		return directory.getName();
	}

	public final Item getNext() throws IOException {
		// pageNo start from 0
		if (pages.size() - 1 > pageNo) {
			return ItemManager.getHeadItem(c, directory, request, pageNo + 1);
		} else {
			return null;
		}
	}

	public final int getOrder() {
		return 0;
	}

	public Integer[] getPageCount() throws IOException {
		if (itemSet.size() == 0) {
			setItemSet();
		}
		return pages.toArray(new Integer[0]);
	}

	public final Item getPrev() throws IOException {
		if (pageNo > 0) {
			return ItemManager.getHeadItem(c, directory, request, pageNo - 1);
		} else {
			return null;
		}
	}

	// TODO query sub dir??
	public final String getThumbnail() {
		final List<File> childs = new ArrayList<File>(Arrays.asList(directory
				.listFiles(c.config.displayFilter())));
		Collections.shuffle(childs);
		for (int fileCount = 0; fileCount < childs.size(); fileCount++) {
			final Content content = ContentManager.getContent(c,
					childs.get(fileCount));
			if (content.getThumbnail() != null) {
				return content.getThumbnail();
			}
		}
		return null;
	}

	public final String getUrl() throws IOException {
		return getUrl(new Integer(pageNo));
	}

	public final String getUrl(final Integer thisPageNo) throws IOException {
		final String url = UrlUtil.getUrl(c.config, directory);
		final Map<String, String> map = new HashMap<String, String>();
		if (thisPageNo.intValue() > 0) {
			map.put("pageno", thisPageNo.toString());
		}
		return UrlUtil.appendParameter(url, map);
	}

	private boolean match(final String relativePath, final String element) {
		if ("titlesearch".equals(request.getParameter("actioncode"))) {
			final String searchTerm = request.getParameter("value");
			if (searchTerm.length() > element.length()) {
				return false;
			}
			final boolean matches = element.contains(searchTerm);
			return matches;
		} else {
			final boolean startsWith = element.startsWith(relativePath);
			return startsWith;
		}
	}

	// TODO multi-thread access
	// TODO really, really, need to make the logic better...
	// TODO add testcase for not use
	// final boolean inRange = noOfItem >= start && noOfItem < end;
	private void setItemSet() throws IOException {
		if (request.isAdminEnable()) {
			final File[] files = directory.listFiles(c.config.displayFilter());
			for (int i = 0; i < files.length; i++) {
				itemSet.add(ItemManager.getItem(c, files[i]));
			}
		} else {
			final long init = System.currentTimeMillis();

			final int start = itemsOfPage * pageNo;
			final int end = itemsOfPage * (pageNo + 1);
			final String relativePath = UrlUtil.getRelativePath(c.config,
					directory);
			int noOfItem = 0;
			for (int i = 0; i < Directory.allItems.length; i++) {
				final String element = Directory.allItems[i];
				if (match(relativePath, element)) {
					final boolean inRange = noOfItem >= start && noOfItem < end;
					if (inRange) {
						final File file = c.config.getRoot(element);
						itemSet.add(ItemManager.getItem(c, file));
					}
					noOfItem++;
					// Don't loop all for total count if it is cached
					if (noOfItem / itemsOfPage == 10) {
						break;
					}
				}
			}

			for (int i = 0; i < noOfItem / itemsOfPage + 1; i++) {
				pages.add(i);
			}
			if (pages.size() <= 1) {
				pages.clear();
			}

			if (itemSet.size() == 0) {
				final File[] files = directory.listFiles(c.config
						.displayFilter());
				if (files != null) {
					for (int i = 0; i < files.length; i++) {
						if (files[i].isFile()) {
							itemSet.add(ItemManager.getItem(c, files[i]));
						}
					}
				}
			}
			LogUtil.log("time taken for select: " + directory.getAbsolutePath()
					+ "\t time is: " + (System.currentTimeMillis() - init)
					/ 1000d + "s");
		}
	}

	@Override
	public final String toString() {
		return "directory: " + directory;
	}
}